package nrpc.message;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Map;
import java.util.function.Consumer;

import message.AbstractMessageBuilder;
import message.MessageBuilder;
import message.MessageBuilderSupplier;
import message.preset.StringMessageBuilder;
import message.reflect.HandlerMapper;
import nrpc.message.meta.InvokeMessage;
import nrpc.util.AsynMethodTool;

public class InvokeMessageBuilderSupplier implements MessageBuilderSupplier {

	private HandlerMapper mapper;
	private Map<String, Method> methods;
	
	public InvokeMessageBuilderSupplier(HandlerMapper mapper, Map<String, Method> methods) {
		this.mapper = mapper;
		this.methods = methods;
	}
	
	@Override
	public MessageBuilder get(Consumer<? super Object> callback) {
		return new AbstractMessageBuilder(callback) {
			int step = 0;
			int i = 0;
			int len;
			MessageBuilder[] builders;
			{
				this.build = new InvokeMessage();
			}
			StringMessageBuilder sBuilder = new StringMessageBuilder((x)->{
				Method m = methods.get(x);
				Parameter[] raw = m.getParameters();
				((InvokeMessage)this.build).name = (String)x;
				
//				System.out.println(x);
				// 异步回调调用忽略最后一个参数
				if(AsynMethodTool.isAsyn(m) && AsynMethodTool.isCallback(m)) len = raw.length-1;
				else len = raw.length;
				
				builders = new MessageBuilder[len];
				((InvokeMessage)this.build).args = new ArrayList<>();
				for(int i = 0; i<len; i++) {
					builders[i] = mapper.getBuilderSupplier(raw[i].getParameterizedType()).get((a)->{
						((InvokeMessage)this.build).args.add(a);
					});
				}
				
			}, mapper);
			@Override
			public boolean read(ByteBuffer byteBuffer) {
				
				switch(step) {
				case 0:
					try {
						((InvokeMessage)this.build).label = byteBuffer.getInt();
					} catch (BufferUnderflowException e) {
						return false;
					}
					step++;
				case 1:
					if(!(sBuilder.read(byteBuffer))) return false;
					step++;
				case 2:
					while(i < len){
						if(!(builders[i].read(byteBuffer))) return false;
						i++;
					}
					step++;
				}
				return super.read(byteBuffer);
			}
		};
	}

}
